package com.thiswhyme.common.utils.json.gson;

import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.thiswhyme.common.expection.BusinessException;
import com.thiswhyme.common.utils.json.gson.strategy.IgnoreAnnotationExclusionStrategy;
import com.thiswhyme.common.utils.json.gson.typeadapter.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;

/**
 * @description: Json工具-Gson
 * @author: thiswhyme
 * @date: 2020/9/17
 */
@Slf4j
public class GsonUtils {

    private static final GsonBuilder commonGsonBuilder = GsonUtils.createCommonBuilder();

    /**
     * 使用常用配置创建一个GsonBuilder实例
     * @return
     */
    public static GsonBuilder createCommonBuilder() {
        GsonBuilder gsonBuilder = new GsonBuilder()
                .serializeNulls()
                .addSerializationExclusionStrategy(new IgnoreAnnotationExclusionStrategy())
                .addDeserializationExclusionStrategy(new IgnoreAnnotationExclusionStrategy())
                .registerTypeHierarchyAdapter(Date.class, new DateSerializerAndDeserializer())
                .registerTypeHierarchyAdapter(Calendar.class, new CalendarSerializerAndDeserializer())
                .registerTypeAdapter(Instant.class, new InstantSerializer())
                .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeSerializer())
                .registerTypeAdapter(LocalDate.class, new LocalDateSerializer())
                .disableInnerClassSerialization();
        return gsonBuilder;
    }

    /**
     * 获取一个根据公用配置创建的Gson对象
     * @return
     */
    public static Gson commonGson() {
        return createCommonBuilder().create();
    }

    /**
     * 获取一个格式化的输出的Gson对象
     * @return
     */
    public static Gson prettyGson() {
        return createCommonBuilder().setPrettyPrinting().create();
    }

    /**
     * 获取转化的json的fieldName是下划线连接的Gson对象
     *<ul>
     *   <li>someFieldName ---> some_field_name</li>
     *   <li>_someFieldName ---> _some_field_name</li>
     *   <li>aStringField ---> a_string_field</li>
     *   <li>aURL ---> a_u_r_l</li>
     * </ul>
     * @return
     */
    public static Gson underScoresGson() {
        return createCommonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
    }

    /**
     * 对象转json
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> String obj2String(T obj) {
        if (obj == null){
            return null;
        }
        try {
            return commonGson().toJson(obj);
        } catch (JsonSyntaxException e) {
            log.error("对象转json失败", e);
            throw new BusinessException("对象转json失败");
        }
    }

    /**
     * 对象转json，并格式化的输出
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> String obj2StringPretty(T obj){
        if (obj == null){
            return null;
        }
        try {
            return prettyGson().toJson(obj);
        } catch (JsonSyntaxException e) {
            log.error("对象转json失败", e);
            throw new BusinessException("对象转json失败");
        }
    }

    /**
     * json转对象
     * @param json
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T string2Obj(String json, Class<T> clazz) {
        if (StringUtils.isEmpty(json) || clazz == null){
            return null;
        }
        try {
            return commonGson().fromJson(json, clazz);
        } catch (JsonSyntaxException e) {
            log.error("json转对象失败", e);
            throw new BusinessException("json转对象失败");
        }
    }

    /**
     * json转对象
     * @param json
     * @param typeToken
     * @param <T>
     * @return
     */
    public static <T> T string2Obj(String json, TypeToken<T> typeToken) {
        if (StringUtils.isEmpty(json) || typeToken == null){
            return null;
        }
        try {
            return commonGson().fromJson(json, typeToken.getType());
        } catch (JsonSyntaxException e) {
            log.error("json转对象失败", e);
            throw new BusinessException("json转对象失败");
        }
    }
}
